package com.hoo.permission.sdk.web.security.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.hoo.permission.sdk.web.properties.JwtProperties;
import com.hoo.permission.sdk.server.domain.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * jwt工具
 * jwt刷新机制： https://www.sundayfine.com/jwt-refresh-token/
 * - 考虑页面多个请求，被替换的 老token 还要预留有效时间
 *
 * -- 还一种：前端处理token刷新 【即将失效:,在用,】
 * @author hank
 * @create 2020-11-12 下午4:47
 **/
@Configuration
@EnableConfigurationProperties({JwtProperties.class})
@Component
public class HooJwtTools {

    @Autowired
    JwtProperties jwtConfig;

    /**
     * 生成签名
     * @param user
     * @return
     */
    public String sign(LoginUser user) {
        Date expiresAt = new Date((new Date()).getTime() + jwtConfig.getExpire() * 1000);
        Map<String,Object> header=new HashMap<>(2);
        header.put("typ","JWT");
        header.put("alg","HS256");
        return JWT.create()
                .withHeader(header)
                .withClaim("userId", user.getId())
                .withClaim("username", user.getUsername())
                .withExpiresAt(expiresAt)
                .sign(Algorithm.HMAC256(StringUtils.isEmpty(jwtConfig.getSecret()) ? user.getPassword() : jwtConfig.getSecret()));
    }

    public boolean refreshToken(String jwtToken) {
        // 刷新并延长 token 有效期
        return true;
    }

    /**
     * 校验token是否过期
     * @return
     */
    public boolean isExpired(String jwtToken) {
        DecodedJWT jwt = JWT.decode(jwtToken);
        return jwt.getExpiresAt().before(new Date());
    }

    public boolean verify(String token, LoginUser user) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(StringUtils.isEmpty(jwtConfig.getSecret()) ? user.getPassword() : jwtConfig.getSecret());
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("userId", user.getId())
                    .withClaim("username", user.getUsername())
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return jwt != null;
        } catch (Exception exception) {
            return false;
        }
    }

    public String getJwtToken(HttpServletRequest request) {
        String token = request.getHeader(jwtConfig.getHeaderName());
        if(StringUtils.isEmpty(token)) {
            token = request.getParameter(jwtConfig.getHeaderName());
        }
        return token;
    }

    public LoginUser getLoginUser(HttpServletRequest request) {
        if (this.isAccessAllowed(request)) {
            String token = this.getJwtToken(request);
            return this.getLoginUser(token);
        }
        return null;
    }

    /**
     * 获取登录用户， 密码置空
     * @param token
     * @return
     */
    public LoginUser getLoginUser(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return new LoginUser(jwt.getClaim("userId").asLong(), jwt.getClaim("username").asString());
    }

    /**
     * 判断是否允许访问
     * @param request
     * @return
     */
    public boolean isAccessAllowed(HttpServletRequest request) {
        String token = this.getJwtToken(request);
        if(StringUtils.isEmpty(token)) {
            throw new SecurityException("无合法token值");
        }
        try {
            if(isExpired(token)) {
                throw new SecurityException("token已过期");
            }
        } catch (JWTDecodeException e) {
            throw new SecurityException("该token无效");
        }
        return true;
    }
}
